package main

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"io"
	"log"
	"net/http"
	"os"
	"strconv"
	"sync"
	"time"

	"github.com/davecgh/go-spew/spew"
	"github.com/gorilla/mux"
	"github.com/joho/godotenv"
)

type Block struct {
	Index     int    //索引
	Timestamp string //时间戳
	BPM       int    //一分钟心跳的频率，脉搏
	Hash      string //哈希
	PrevHash  string //前一个哈希
}

var Blockchain []Block //区块链

//定义hash算法 SHA256 hasing
func calculateHash(block Block) string {

	//strconv.Itoa 将整数转换为十进制字符串形式（即：FormatInt(i, 10) 的简写）
	record := strconv.Itoa(block.Index) + block.Timestamp + strconv.Itoa(block.BPM) + block.PrevHash

	h := sha256.New()       //创建一个Hash对象
	h.Write([]byte(record)) //h.Write写入需要哈希的内容
	hashed := h.Sum(nil)    //h.Sum添加额外的[]byte到当前的哈希中，一般不是经常需要这个操作
	return hex.EncodeToString(hashed)
}

//生成块block
func genarateBlock(oldBlock Block, BPM int) (Block, error) {
	var newBlock Block
	t := time.Now()

	newBlock.Index = oldBlock.Index + 1
	newBlock.Timestamp = t.String()
	newBlock.BPM = BPM
	newBlock.PrevHash = oldBlock.Hash
	newBlock.Hash = calculateHash(newBlock)

	return newBlock, nil //此处error为后期完善复杂业务时留用，（可以省略）
}

//验证块，查看是否被篡改
func isBlockValid(newBlock Block, oldBlock Block) bool {
	if newBlock.Index != oldBlock.Index+1 {
		return false
	}
	if newBlock.PrevHash != oldBlock.Hash {
		return false
	}
	if calculateHash(newBlock) != newBlock.Hash {
		return false
	}
	return true
}

//判断链长最长的作为正确的链进行覆盖（作为主链）
func replaceChain(newBlocks []Block) {
	if len(newBlocks) > len(Blockchain) {
		Blockchain = newBlocks
	}
}

// 构建Web Server
func makeMuxRouter() http.Handler {
	muxRouter := mux.NewRouter()
	muxRouter.HandleFunc("/", handleGetBlockchain).Methods("GET")
	muxRouter.HandleFunc("/", handleWriteBlock).Methods("POST")
	return muxRouter
}

// 查询所有区块信息
func handleGetBlockchain(w http.ResponseWriter, r *http.Request) {
	//json.MarshalIndent() - 格式化输出json
	bytes, err := json.MarshalIndent(Blockchain, "", "\t")

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	io.WriteString(w, string(bytes))
}

// 响应json格式化封装
func respondWithJson(w http.ResponseWriter, r *http.Request, code int, payload interface{}) {
	reponse, err := json.MarshalIndent(payload, "", "\t")
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)      //响应错误状态码
		w.Write([]byte("HTTP 500: Internal Server Error")) //响应错误内容
		return
	}
	w.WriteHeader(code)
	w.Write(reponse)
}

type Message struct {
	BPM int //脉搏信息
}

// 创建新区块Block，并返回创建Block的信息
func handleWriteBlock(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	var m Message

	decoder := json.NewDecoder(r.Body)
	if err := decoder.Decode(&m); err != nil {
		respondWithJson(w, r, http.StatusBadRequest, r.Body)
		return
	}
	defer r.Body.Close()

	oldBlock := Blockchain[len(Blockchain)-1]
	mutex.Lock()
	newBlock, err := genarateBlock(oldBlock, m.BPM)
	if err != nil {
		log.Println("newBlock create failed", err.Error())
	}
	mutex.Unlock()

	if isBlockValid(newBlock, oldBlock) {
		Blockchain = append(Blockchain, newBlock) //将新建的块block加入
		spew.Dump(Blockchain)                     //调试使用，将结构打印到控制台
	}

	respondWithJson(w, r, http.StatusCreated, newBlock)

}

// web运行程序
var mutex = &sync.Mutex{} //用于确保线程安全的Lock

func run() error {
	mux := makeMuxRouter() //编写路由

	httpAddr := os.Getenv("PORT")
	log.Println("Listening on", os.Getenv("PORT"))
	s := &http.Server{
		Addr:           ":" + httpAddr,
		Handler:        mux,
		ReadTimeout:    time.Second * 10,
		WriteTimeout:   time.Second * 10,
		MaxHeaderBytes: 1 << 20,
	}

	if err := s.ListenAndServe(); err != nil {
		return err
	}
	return nil
}

//建立主程序入口
func main() {
	err := godotenv.Load("prop.env") //读取根目录中的.env文件
	if err != nil {
		log.Fatal(err)
	}

	go func() {
		t := time.Now()
		genesisBlock := Block{} //创世块的创建，第一个块生成
		genesisBlock = Block{0, t.String(), 0, calculateHash(genesisBlock), ""}
		spew.Dump(genesisBlock)

		mutex.Lock()
		Blockchain = append(Blockchain, genesisBlock)
		mutex.Unlock()

	}()
	log.Fatal(run())
}
